/**
 * 测试用例JSON格式导出器基类
 *
 * Created by snowingsea on 2021/01/20.
 */
const fs = require('fs-extra');
const path = require('path');
const ProjectVersionSpliter = require('./../project-version-spliter');
const common = require('./../../common');
const tests = require('../../../models/tests');
const functions = require('../../../models/functions');
const files = require('../../../models/files');
const File = require('../../../models/mongo/file');
const fileElements = require('../../../models/file-elements');
const utility = require('../../utility');

class BaseTestCasesExporter extends ProjectVersionSpliter {
  constructor(versionId, locale, version = null, project = null, creator = null, fileIds = null) {
    super(versionId, version, project, creator);
    this.i18n = locale;
    this.fileIds = fileIds;
    this.testCaseResult = {};// 测试用例执行结果
    this.allFuncsOfVersion = new Map();// 当前版本下所有函数
    this.allFileIdsOfVersion = new Map();// 当前版本所有文件id
    this.allFilePathOfVersion = new Map();// 当前版本所有文件路径
    this.allFuncVariableStubs = new Map(); // 当前版本所有函数变量中的stubs
  }

  filterTestCasesByResultKind() {
    return false;
  }

  /**
   * 导出用例
   * @return {Promise}
   */
  exportTestCases() {
    return new Promise((resolve, reject) => {
      common.complementByProjectVersionId(this.versionId, this.version, this.project, this.creator)
        .then(resolve)
        .catch(reject);
    });
  }

  /**
     * 获取测试用例结果信息
     * @param {Object} locales 国际化配置
     */
  buildTestCaseResult(testCase) {
    const result = {};
    switch (testCase.result.kind) {
      case tests.resultKind.unknown:
        result.message = {
          kind: this.i18n.__('tests.testResult.unknown'),
          suggestion: '',
        };
        break;
      case tests.resultKind.succeed:
        result.message = {
          kind: this.i18n.__('tests.testResult.succeed'),
          suggestion: '',
        };
        break;
      case tests.resultKind.failed:
        result.message = {
          kind: this.i18n.__('tests.testResult.failed'),
          suggestion: '',
        };
        break;
      case tests.resultKind.noResult:
        result.message = {
          kind: this.i18n.__('tests.testResult.noResult'),
          suggestion: '',
        };
        break;
      case tests.resultKind.nonStrictSucceed:
        result.message = {
          kind: this.i18n.__('tests.testResult.nonStrictSucceed'),
          suggestion: '',
        };
        break;
      case tests.resultKind.runtimeError:
        result.message = {
          kind: this.i18n.__('tests.runtimeError'),
          suggestion: this.i18n.__('tests.runtimeErrorSuggestion'),
        };
        break;
      case tests.resultKind.timeout:
        result.message = {
          kind: this.i18n.__('tests.timeoutError'),
          suggestion: this.i18n.__('tests.timeoutErrorSuggestion'),
        };
        break;
      case tests.resultKind.systemError:
        result.message = {
          kind: this.i18n.__('tests.testResult.runtimeException'),
          suggestion: this.i18n.__('tests.systemErrorSuggestion'),
        };
        break;
      case tests.resultKind.instrumentError:
        result.message = {
          kind: this.i18n.__('tests.testResult.runtimeException'),
          suggestion: this.i18n.__('tests.instrumentErrorSuggestion'),
        };
        break;
      case tests.resultKind.compileError:
        result.message = {
          kind: this.i18n.__('tests.testResult.runtimeException'),
          suggestion: this.i18n.__('tests.compileErrorSuggestion'),
        };
        break;
      default:
        break;
    }
    return result;
  }

  /**
   * 过滤需要导出的文件
   */
  async filterSelectedFiles() {
    const newFiles = new Map();
    await Promise.all(this.fileIds.map(async ({ fileId }) => {
      const file = await File.findOne({ _id: fileId }, {
        _id: 1, path: 1, fileName: 1, kind: 1,
      }).lean();
      if (file.kind === files.kind.directory) {
        const regExp = new RegExp(`^${utility.regExpQuote(file.path.concat('/'))}`);
        const childrenFiles = await File.find({
          versionId: this.versionId, kind: files.kind.file, status: files.statusKind.compiled, path: regExp,
        }, {
          _id: 1, path: 1, fileName: 1, kind: 1,
        }).lean();
        childrenFiles.forEach((f) => {
          if (newFiles.has(f._id.toString())) {
            return;
          }
          newFiles.set(f._id.toString(), f);
        });
      } else {
        newFiles.set(fileId.toString(), file);
      }
    }));
    // 重组导出的文件和函数
    const newFileIds = [];
    newFiles.forEach((value) => {
      for (let i = 0; i < this.fileIds.length; i += 1) {
        if (value._id.toString() === this.fileIds[i].fileId) {
          value.functions = this.fileIds[i].functions;
        }
      }
      newFileIds.push(value);
    });
    this.fileIds = newFileIds;
  }

  /**
   * 创建导出versionInfo配置文件
   * @param {string} projectDir 项目地址
   * @param {string} versionType 版本类型
   * @param {string} exportFormat 导出格式
   * @returns
   */
  createVersionInfoFile(projectDir, versionType, exportFormat) {
    return new Promise((resolve, reject) => {
      const versionInfoFile = path.join(projectDir, 'versionInfo.json');
      fs.pathExists(versionInfoFile)
        .then((file) => {
          if (!file) {
            return fs.outputJson(versionInfoFile, { versionType, exportFormat });
          }
          return Promise.resolve();
        })
        .then(resolve)
        .catch(reject);
    });
  }

  /**
 * 过滤文件中选中的函数
 * @param {object} file 文件
 * @param {object} functions 函数
 */
  filterSelectedFunc(file, functions) {
    let filterFunc = [];
    if (file.functions && file.functions.length > 0) {
      filterFunc = functions.filter((func) => file.functions.includes(func._id.toString()));
    } else {
      filterFunc = functions;
    }
    return filterFunc;
  }

  async getFuncsByVersionId() {
    const funcs = await functions.getFunctions(this.versionId, { kinds: fileElements.getCountableKinds() });
    funcs.forEach((func) => {
      const functionId = func._id.toString();
      if (!this.allFuncsOfVersion.has(functionId)) {
        this.allFuncsOfVersion.set(functionId, func);
      }
    });
  }

  async getFilesByVersionId() {
    const allFiles = await File.find({
      versionId: this.versionId,
      kind: { $in: [files.kind.file, files.kind.fileOutsideProject] },
    }, { _id: 1, fileName: 1, path: 1 });
    allFiles.forEach((file) => {
      const fileId = file._id.toString();
      const obj = {
        _id: file._id,
        fileName: file.fileName,
        path: file.path,
        versionId: file.versionId,
      };
      if (!this.allFileIdsOfVersion.has(fileId)) {
        this.allFileIdsOfVersion.set(fileId, obj);
      }
      if (!this.allFilePathOfVersion.has(file.path)) {
        this.allFilePathOfVersion.set(file.path, obj);
      }
    });
  }

  async getAllFunctionVariablesStubsByVersionId() {
    this.allFuncVariableStubs = await functions.getFunctionStubsByVersionId(this.versionId);
  }
}

/**
 * Export model definition object.
 */
module.exports = BaseTestCasesExporter;
